﻿using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wintellect.PowerCollections;

namespace BullsAndCowsGameUT
{
    class BullsAndCowsGame
    {
        #region Fields

        private int bulls;
        private int cows;
        private int attempts;
        private int cheats;
        private string name;
        private string secretNumber;
        private string input;
        private int[] helpMask;

        private OrderedMultiDictionary<int, string> scoreboard;

        #endregion

        #region Properties

        public int Bulls
        {
            get
            {
                return bulls;
            }
            set
            {
                bulls = value;
            }
        }

        public int Cows
        {
            get
            {
                return cows;
            }
            set
            {
                cows = value;
            }
        }

        public int Attempts
        {
            get
            {
                return attempts;
            }
            set
            {
                attempts = value;
            }
        }

        public int Cheats
        {
            get
            {
                return cheats;
            }
            set
            {
                cheats = value;
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public string SecretNumber
        {
            get
            {
                return secretNumber;
            }
            set
            {
                secretNumber = value;
            }
        }

        public string Input
        {
            get
            {
                return input;
            }
            set
            {
                input = value;
            }
        }

        public int[] HelpMask
        {
            get
            {
                return helpMask;
            }
            set
            {
                helpMask = value;
            }
        }

        public OrderedMultiDictionary<int, string> Scoreboard
        {
            get
            {
                return scoreboard;
            }
            set
            {
                scoreboard = value;
            }
        }

        #endregion

        #region Methods

        public BullsAndCowsGame()
        {
            InitializeParameters();
            scoreboard = new OrderedMultiDictionary<int, string>(true);
        }

        public void InitializeParameters()
        {
            this.Bulls = 0;
            this.Cows = 0;
            this.Attempts = 0;
            this.Cheats = 0;
            this.name = "";
            this.SecretNumber = GenerateSecretNumber();
            helpMask = new int[] { 0, 0, 0, 0 };
            this.input = "Game on";

        }

        public string GenerateSecretNumber()
        {
            Random rnd = new Random();
            int secret = rnd.Next() % 10000;
            StringBuilder result = new StringBuilder(secret.ToString());
            while (result.Length < 4)
            {
                int num = rnd.Next() % 10;
                result.Append(num.ToString());
            }
            return result.ToString();
        }

        public void AddToScoreboard(string name)
        {
            scoreboard.Add(attempts, name);
            if (scoreboard.Values.Count() > 5)
            {
                int key = scoreboard.Keys.Max();
                string player = scoreboard[key].First();
                scoreboard[key].Remove(player);
            }
        }

        public bool InputIsValid()
        {
            if (input == "restart" || input == "help" || input == "exit" || input == "top")
            {
                return true;
            }
            int num = 0;
            int.TryParse(input, out num);
            if (input.Length == 4 && num != 0)
            {
                return true;
            }
            throw new ArgumentException("Invalid input");
        }

        public void FindBullsCows()
        {
            bulls = 0;
            cows = 0;
            StringBuilder secret = new StringBuilder(secretNumber);
            StringBuilder inputStr = new StringBuilder(input);
            for (int i = 0; i < 4; i++)
            {
                if (secret[i] == inputStr[i])
                {
                    bulls++;
                    secret[i] = 'X';
                    inputStr[i] = 'Y';
                }
            }
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (inputStr[j] == secret[i])
                    {
                        cows++;
                        inputStr[j] = 'Y';
                        secret[i] = 'X';
                        break;
                    }
                }
            }
        }

        public void Display()
        {
            if (bulls != 4)
            {
                Console.Write(string.Format("Wrong number! Bulls: {0}, Cows: {1}\nEnter your guess or command: ", bulls, cows));
            }
            else
            {
                if (cheats > 0)
                {
                    Console.Write(string.Format("Congratulations! You guessed the secret number in {0} attempts and {1} cheats." +
                        "\nYou are not allowed to enter the top scoreboard.", attempts, cheats));
                    Console.ReadLine();
                }
                else
                {
                    Console.Write(string.Format("Congratulations! You guessed the secret number in {0} attempts.", attempts));
                    if (scoreboard.Count > 0)
                    {
                        if ((scoreboard.Keys.Min()) <= attempts)
                        {
                            Console.Write("\nPlease enter your name for the top scoreboard: ");
                            AddToScoreboard(Console.ReadLine());
                        }
                    }
                    else
                    {
                        Console.Write("\nPlease enter your name for the top scoreboard: ");
                        AddToScoreboard(Console.ReadLine());
                    }
                }
            }
        }

        public void DisplayHelp()
        {
            if (cheats >= 4)
            {
                Console.WriteLine("Are you blind?");
                return;
            }
            Random rnd = new Random();
            int helpPosition = rnd.Next() % 4;
            while (helpMask[helpPosition] != 0)
            {
                helpPosition = rnd.Next() % 4;
            }
            helpMask[helpPosition] = 1;
            Console.WriteLine("The number looks like ");
            for (int i = 0; i < helpMask.Length; i++)
            {
                if (helpMask[i] == 1)
                {
                    Console.Write(secretNumber[i]);
                }
                else
                {
                    Console.Write('X');
                }
            }
            cheats++;
            Console.Write("\nEnter your guess or command: ");
        }

        public void DisplayTop()
        {
            int i = 0;
            foreach (var item in scoreboard)
            {
                foreach (var player in item.Value)
                {
                    i++;
                    Console.WriteLine(string.Format("{0}. {1} --> {2} attempts", i, player, item.Key));
                }
            }
            Console.Write("Enter your guess or command: ");
        }

        public void SayHello()
        {
            Console.WriteLine("Welcome to “Bulls and Cows” game. Please try to guess my secret 4-digit number.");
            Console.Write("Use 'top' to view the top scoreboard, 'restart' to start a new game and 'help'\n" +
                "to cheat and 'exit' to quit the game.\nEnter your guess or command: ");
        }

        public string GetInput()
        {
            return this.Input = Console.ReadLine().ToLower();
        }

        public bool Winner()
        {
            if (Bulls == 4)
            {
                return true;
            }
            return false;
        }

        public void Play()
        {
            while (true)
            {
                Console.Clear();
                InitializeParameters();
                SayHello();
                {
                    while (true)
                    {
                        GetInput();
                        if (InputIsValid())
                        {
                            if (input == "top")
                            {
                                DisplayTop();
                                continue;
                            }
                            else if (input == "exit")
                            {
                                return;
                            }
                            else if (input == "help")
                            {
                                DisplayHelp();
                                continue;
                            }
                            else if (input == "restart")
                            {
                                break;
                            }
                            else
                            {
                                FindBullsCows();
                                Attempts++;
                                Display();
                                if (Winner())
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion
    }
}
